package com.puccampinas.finalproject.rest;

import java.util.Properties;

import org.apache.log4j.Logger;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

import com.sun.jersey.spi.container.servlet.ServletContainer;

public class WebServerManager implements Runnable {

    private static final Logger SYSTEM_LOGGER = Logger.getLogger("SYSTEM");

    private static final Logger EXCEPTION_LOGGER = Logger.getLogger("EXCEPTIONS");

    @SuppressWarnings("unused")
	private Server webServer;

    private Properties configuration;

    public WebServerManager(Properties smartProperties) {
        this.configuration = smartProperties;
    }

    public void run() {

        SYSTEM_LOGGER.info("Starting server for RESTful WebServices");

        try {
            webServer = startServer();
            SYSTEM_LOGGER.info("JAX-RS Jersey app started.");

            try {
                synchronized (this) {
                    this.wait();
                }
            } catch (InterruptedException e) {
                // Do nothing
                SYSTEM_LOGGER.warn(e.getMessage());
            }

        } catch (Exception e) {
            EXCEPTION_LOGGER.error("Exception on Jersey init: " + e.getMessage(), e);
        }
    }

    protected Server startServer() throws Exception {

        Server server = null;

        try {
            server = new Server();

            // Create connector
            Connector connector = configureConnector(Integer.parseInt(configuration.getProperty("http.server.port", "12012")));
            server.addConnector(connector);

            // Create servlet Holder
            ServletHolder servletHolder = new ServletHolder(ServletContainer.class);
            servletHolder.setInitParameter("com.sun.jersey.config.property.packages", "com.puccampinas.finalproject.rest");

            // Create context
            ContextHandlerCollection contexts = new ContextHandlerCollection();
            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
            context.addServlet(servletHolder, "/*");
            context.setContextPath("/");

            String[] connectorName = {connector.getName()};
            context.setConnectorNames(connectorName);
            contexts.addHandler(context);

            // Add all contexts to server
            server.setHandler(contexts);

            // Start server
            server.start();

        } catch (Exception e) {
            SYSTEM_LOGGER.fatal("Error starting Jetty webserver: " + e.toString());
            System.exit(1);
        }

        return server;
    }

    private SelectChannelConnector configureConnector(int port) {

        SYSTEM_LOGGER.info("Starting WebServer[" + configuration.getProperty("http.server.name", "jetty") + "] on port " + port + "...");

        SelectChannelConnector connector = new SelectChannelConnector();

        // set port
        connector.setPort(port);
        connector.setName("port" + String.valueOf(port));

        int maxIdleTime = Integer.parseInt(configuration.getProperty("http.server.maxIdleTime", "30000"));
        SYSTEM_LOGGER.info("Webserver max idle time: " + maxIdleTime);
        connector.setMaxIdleTime(maxIdleTime);

        int threadPoolNumber = Integer.parseInt(configuration.getProperty("http.server.threadPool", "4"));
        int minThreads = Integer.parseInt(configuration.getProperty("http.server.threadPool.minThreads", "10"));
        int maxThreads = Integer.parseInt(configuration.getProperty("http.server.threadPool.maxThreads", "100"));

        SYSTEM_LOGGER.info("Webserver thread Pool: " + threadPoolNumber);
        QueuedThreadPool threadPool = new QueuedThreadPool(threadPoolNumber);
        threadPool.setMinThreads(minThreads);
        threadPool.setMaxThreads(maxThreads);
        connector.setThreadPool(threadPool);

        int acceptQueueSize = Integer.parseInt(configuration.getProperty("http.server.acceptQueueSize", "1000"));
        SYSTEM_LOGGER.info("Webserver accept Queue Size: " + acceptQueueSize);
        connector.setAcceptQueueSize(acceptQueueSize);

        int acceptorSize = Integer.parseInt(configuration.getProperty("http.server.acceptorsSize", "16"));
        SYSTEM_LOGGER.info("Webserver acceptors size: " + acceptorSize);
        connector.setAcceptors(acceptorSize);

        return connector;
    }
}
